package com.klikmee.sms.core.base.postman;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Date;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.BindingProvider;

import org.apache.log4j.Logger;

import com.klikmee.sms.cef.services.ws.ServiceInterface;
import com.klikmee.sms.cef.services.ws.ServiceInterfaceService;
import com.klikmee.sms.cef.services.ws.ServiceRequestMessage;
import com.klikmee.sms.cef.services.ws.ServiceResponseReport;
import com.klikmee.sms.cef.services.ws.SubscriptionInfo;
import com.klikmee.sms.core.base.util.CoreUtilities;
import com.klikmee.sms.core.base.util.States;
import com.klikmee.sms.core.model.IncomingMessage;
import com.klikmee.sms.core.model.OutgoingMessage;
import com.klikmee.sms.core.model.Service;

public class SimpleMessageDeliverer implements ServiceMessageDeliverer {

	private URL					wsdlURL			= null;
	private ServiceInterface	serviceProxy	= null;
	private Logger				logger			= Logger.getLogger( SimpleMessageDeliverer.class );

	public void init() {
		serviceProxy = new ServiceInterfaceService( this.wsdlURL ).getServiceInterfacePort();
		logger.info( "Simple message deliverer initialized." );
	}

	/**
	 * Tries to deliver message to the service. Service endpoint address is validated first.
	 * 
	 * @param incomingMessage
	 *            message to be delivered to service
	 * @return true if message is successfully delivered, otherwise false
	 */
	public boolean deliverMessage( IncomingMessage incomingMessage ) {

		// validate URL
		String endpointURL = incomingMessage.getService().getEndpoint();
		if (!validateURL( endpointURL )) {
			// validation failed, return false
			return false;
		}

		// override addres in proxy
		overrideEndpointAddress( endpointURL );

		// deliver message to service
		ServiceRequestMessage serviceRequestMessage = this.createServiceRequestMessage( incomingMessage );

		try {
			serviceProxy.onRequestMessage( serviceRequestMessage );
			// if everything OK, return true
			return true;
		} catch (Exception e) {
			logger.fatal( "Failed to deliver message to service", e );
			return false;
		}

	}

	public ServiceRequestMessage createServiceRequestMessage( IncomingMessage incomingMessage ) {
		ServiceRequestMessage message = new ServiceRequestMessage();
		message.setCoreId( incomingMessage.getId() );
		message.setFromNumber( incomingMessage.getFromNumber() );
		message.setMessageText( incomingMessage.getMessage() );
		message.setTimeReceived( CoreUtilities.convertToXMLGregorianCalendar( incomingMessage.getOurIncomingTime() ) );
		message.setToNumber( incomingMessage.getToNumber() );

		return message;

	}

	private void overrideEndpointAddress( String newEndpointAddress ) {
		BindingProvider provider = (BindingProvider) serviceProxy;
		provider.getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, newEndpointAddress );
	}

	public boolean validateURL( String address ) {
		//try to construct URL out of address
		try {
			URL url = new URL( address );
			// if this is ok check more
			url.toURI();

			// if no exceptions are raised, return true
			return true;
		} catch (MalformedURLException e) {
			// bad URL return false
			return false;
		} catch (URISyntaxException e) {
			// bad URL, return false
			return false;
		}

	}

	/**
	 * Forwards status report to
	 * 
	 * @param outgoingMessage
	 *            obj containing status report data
	 */
	public boolean forwardDeliveryReport( OutgoingMessage outgoingMessage ) {

		String endpoint = outgoingMessage.getService().getEndpoint();

		// if end point URL is null then skip 
		if (endpoint == null) {
			return true;
		}
		// validate URL
		if (!validateURL( endpoint )) {
			return false;
		}

		// build ResponseReport obj
		ServiceResponseReport responseReport = createServiceResponseReport( outgoingMessage );

		overrideEndpointAddress( endpoint );

		// try to deliver report
		try {
			serviceProxy.onResponseReport( responseReport );
			return true;
		} catch (Exception e) {
			logger.fatal( String.format( "Failed to deliver status report to service, outgoingmessage id = [%s]", outgoingMessage.getId() ), e );
			return false;
		}

	}

	private ServiceResponseReport createServiceResponseReport( OutgoingMessage outgoingMessage ) {
		ServiceResponseReport report = new ServiceResponseReport();
		report.setResponseId( outgoingMessage.getId() );
		report.setStatus( outgoingMessage.getState().equals( States.OutgoingMessage.DELIVERED ) ? "DELIVERED" : "NOT DELIVERED" );

		return report;
	}

	/**
	 * Forwards approved subscription request to service instance.
	 */
	public boolean forwardSubscripitonRequest( Service service, String distributionListId, String msisdn, Date validTo ) {

		String endpoint = service.getEndpoint();
		// if enpoint is null then skip
		if (endpoint == null) {
			return true;
		}

		if(!validateURL( endpoint )){
			return false;
		}
		
		// build subscription info
		SubscriptionInfo subscriptionInfo = createSubscriptionInfo( distributionListId, msisdn, validTo );
		
		try {
			serviceProxy.onSubscriptionApproved( subscriptionInfo );
			return true;
		} catch (Exception e) {
			logger.fatal( String.format( "Failed to forward subscription request to service, distributionListId = [%s], msisdn = [%s] ", distributionListId, msisdn), e );
			return false;
		}
		
	}

	/**
	 * Utility method for construction {@link SubscriptionInfo}
	 * @param distributionListId
	 * @param msisdn
	 * @param validTo
	 * @return
	 */
	private SubscriptionInfo createSubscriptionInfo( String distributionListId, String msisdn, Date validTo ) {
		SubscriptionInfo subscriptionInfo = new SubscriptionInfo();
		subscriptionInfo.setDistributionListId( distributionListId );
		subscriptionInfo.setMsisdn( msisdn );
		subscriptionInfo.setValidTo( CoreUtilities.convertToXMLGregorianCalendar( validTo ) );

		return subscriptionInfo;

	}

	public void setWsdlURL( URL wsdl ) {
		this.wsdlURL = wsdl;
	}

}
